// =====================================================================================
//
//       Filename:  application.cpp
//
//    Description:  Implémentation des objets communs de l'application (hors interfaces
//					spécifiques)
//
//        Version:  1.0
//        Created:  26/08/2009 15:59:00
//       Revision:  none
//       Compiler:  g++
//
//         Author:  François Hissel (fh), francois.hissel@m4x.org
//        Company:
//
// =====================================================================================

#include	<iostream>
#include	<sstream>
#include	<string>
#include	<list>
#include	<vector>
#include	<cmath>
#include	<cfloat>
#include	<cstdlib>
#include	<ctime>
#include	"kdtree.h"
#include	"erreurs.h"
#include	"config.h"
#include	"raster.h"
#include	"geometrie.h"
#include	"graphe.h"
#include	"antcolony.h"
#include	"routes.h"
#include	"application.h"

using namespace std;

/**********************************************/
/* FONCTIONS DIVERSES UTILISEES PAR LE MODULE */
/**********************************************/
static const double penalite=5;	//Pénalité imposée lors du calcul de la distance de deux points en passant par le réseau. La distance sur le réseau routier est la distance réelle. En dehors du réseau routier, la distance est multipliée par le coefficient de pénalité afin de prendre en compte le temps supplémentaire mis lorsque le véhicule est quitté.

/**
 * \fn bool accessible(const Intersection *i)
 * \brief Indique si l'intersection est accessible à partir du réseau routier
 *
 * La fonction indique si l'intersection spécifiée en argument est accessible à partir des sources choisies en passant par le réseau routier. Pour ce faire, elle vérifie son champ Intersection::dist. Cette fonction ne fonctionne que si l'algorithme de calcul des distances sur le réseau a déjà été lancé.
 * \param i Pointeur vers l'intersection à tester
 * \return Vrai si l'intersection est accessible, faux sinon
 */
bool accessible(const Intersection *i) {
	return i->dist>=0;
}

/**
 * \fn double heuristique(const Sommet *a,const Sommet *b)
 * \brief Fonction heuristique pour l'estimation de la distance entre deux sommets
 *
 * La fonction retourne la distance entre les sommets a et b. Il s'agit d'une heuristique pour l'estimation de la distance restante à la destination utilisée dans l'algorithme A*.
 * \param a Premier sommet
 * \param b Second sommet
 * \return Distance euclidienne entre les deux sommets
 */
double heuristique(const Sommet *a,const Sommet *b) {
	return ((Intersection*)a)->distance(*(Point*)((Intersection*)b));
}

/********************************************/
/*  IMPLEMENTATION DE LA CLASSE APPLICATION */
/********************************************/
Application::Application(int pargc,char **pargv,DisplayProc paffiche,DisplayProc paffiche_erreur):argc(pargc),argv(pargv),affiche(paffiche),affiche_erreur(paffiche_erreur),reseau(0),raster(0),calcule_raster(true),max_width(800),max_height(800),raster_size(400),affiche_legende(false) {
	// Initialisation
	srand(time(0));
	reseau=new Reseau;
	// Définition des attributs d'une source et d'un enjeu
	_dtsource=new DescriptionTable(1);
	_dtsource->ajoute_champ(Champ(Champ::CHAINE,"Nom"));
	_dtenjeu=new DescriptionTable(1);
	_dtenjeu->ajoute_champ(Champ(Champ::CHAINE,"Nom"));
}

Application::~Application() {
	delete raster;
	delete reseau;
	vide_sources();
	vide_enjeux();
	delete _dtsource;
	delete _dtenjeu;
}

void Application::ajoute_source(double px,double py,string pnom) {
	Ponctuel *p=new Ponctuel(px,py,_dtsource);
	p->set(0,pnom);
	sources.push_back(p);
}

void Application::vide_sources() {
	for (list<Ponctuel*>::iterator it=sources.begin();it!=sources.end();it++) delete (*it);
	sources.clear();
}

void Application::ajoute_enjeu(double px,double py,string pnom) {
	Ponctuel *p=new Ponctuel(px,py,_dtenjeu);
	p->set(0,pnom);
	enjeux.push_back(p);
}

void Application::vide_enjeux() {
	for (list<Ponctuel*>::iterator it=enjeux.begin();it!=enjeux.end();it++) delete (*it);
	enjeux.clear();
}

double Application::evalue_cote(double x,double y) {
	if (niveaux.size()==0) return -1.0;
	if (niveaux.size()==1) return niveaux.front();
	list<Point>::const_iterator it=echelles.begin();
	list<double>::const_iterator itn=niveaux.begin();
	Point p1=*it;	// Point courant
	double niv1=*itn;	// Niveau d'eau courant
	double dist=DBL_MAX;	// Distance minimale trouvée
	double val=0.0;	// Valeur de la cote trouvée pour la précédente distance minimale
	double lambda;
	double d1,d2;
	it++;itn++;
	while (it!=echelles.end()) {
		d1=(it->x-p1.x);
		d2=(it->y-p1.y);
		lambda=(d1*(x-p1.x)+d2*(y-p1.y))/(d1*d1+d2*d2);
		if (lambda<0) lambda=0;
		else if (lambda>1) lambda=1;
		d1=x-p1.x-lambda*(it->x-p1.x);
		d2=y-p1.y-lambda*(it->y-p1.y);
		d1=d1*d1+d2*d2;	// Distance au carré au segment de droite
		if (d1<dist) {
			dist=d1;
			val=niv1+lambda*(*itn-niv1);
		}
		p1=*it;
		niv1=*itn;
		it++;itn++;
	}
	return val;
}

void Application::actualise_coupure(int champ_zini,int champ_zfin) {
	if (config.champ_zini<0 || config.champ_zfin<0) return;	// Si l'une des cotes n'est pas disponible, on ne peut pas effectuer le calcul
	for (vector<Arc*>::iterator it=reseau->arcs.begin();it!=reseau->arcs.end();it++) {
		Troncon* t=(Troncon*)(*it);
		double zini=evalue_cote(t->front()->x,t->front()->y);
		double zend=evalue_cote(t->back()->x,t->back()->y);
		if (t->get_double(config.champ_zini)<zini || t->get_double(config.champ_zfin)<zend) t->coupe=true;
		else t->coupe=false;
	}
}

void Application::calcule_poids() {
	actualise_coupure(config.champ_zini,config.champ_zfin);
}

void Application::cherche_itineraire(Sommet* s1,Sommet* s2) {
	list<Arc*> chemin=reseau->itineraire(s1,s2,heuristique);
	if (chemin.size()==0) affiche("Il n'existe pas d'itinéraire entre le point de départ et le point d'arrivée",INFORMATION,"Itinéraire introuvable");
	double poids=0.0;
	ostringstream oss;
	ostringstream oss2;

	ofstream fichier;
	string tmp = "";

	if(config.fic_osiris_itin != ""){
		fichier.open(config.fic_osiris_itin.c_str(), ios::out | ios::trunc);

		if(!fichier.is_open()) return;
	}

	oss << "Itinéraire :" << endl;
	for (list<Arc*>::iterator it=chemin.begin();it!=chemin.end();it++) {
		((Troncon*)(*it))->tag=1;
		//cout << (*it)->orig->id << " - " << (*it)->dest->id << " : " << (*it)->poids << " u\n";
		Troncon *arc=(Troncon*)(*it);

		if(config.fic_osiris_itin != ""){
			for (deque<Point*>::iterator itt = arc->begin(); itt!=arc->end();itt++) {
				Point *p=(Point*)(*itt);
				oss2 << std::fixed << p->x << "," << -(p->y);
				tmp += oss2.str() + " ";
				oss2.str("");
			}
			tmp += "^";
		}

		if(config.weight_unit == "h:m:s") oss << convert_time(arc->poids);
		else if(config.weight_unit != "") oss << arc->poids << config.weight_unit;
		else oss << arc->poids << "u";

		for (list<string>::iterator its=affichage_troncon.begin();its!=affichage_troncon.end();its++) {
			oss << ", ";
			arc->display(oss,(*its));
		}
		oss << "\n";
		poids+=(*it)->poids;
	}
	if(config.weight_unit == "h:m:s") oss << "TEMPS total : " << convert_time(poids) << endl;
	else if(config.weight_unit != "") oss << "Poids total : " << poids << config.weight_unit << endl;
	else oss << "Poids total : " << poids << "u" << endl;

	if(config.fic_osiris_itin != ""){
		fichier << tmp << endl;
		fichier << oss.str();
		fichier.close();
	}
	affiche(oss.str(),TEXTE,"");
}

void Application::calcule_ratio(int maxwidth,int maxheight,bool fixed) {
	if (w_xmin>=w_xmax || w_ymin>=w_ymax) {
		w_xmin=reseau->xmin;
		w_xmax=reseau->xmax;
		w_ymin=reseau->ymin;
		w_ymax=reseau->ymax;
	}
	double rx=maxwidth/(w_xmax-w_xmin);
	double ry=maxheight/(w_ymax-w_ymin);
	// Le ratio est calculé pour que l'ensemble de la zone au moins puisse être affiché dans la fenêtre. Il s'agit donc du maximum des ratios obtenus respectivement en abscisse et en ordonnée.
	if (rx<ry) {
		ratio=rx;
		double ecart_y=fixed?(maxheight/ratio-w_ymax+w_ymin)/2:0;
		wxmin=w_xmin;
		wymin=w_ymin-ecart_y;
		wxmax=w_xmax;
		wymax=w_ymax+ecart_y;
	} else {
		ratio=ry;
		wymin=w_ymin;
		double ecart_x=fixed?(maxwidth/ratio-w_xmax+w_xmin)/2:0;
		wxmin=w_xmin-ecart_x;
		wymax=w_ymax;
		wxmax=w_xmax+ecart_x;
	}
}

void Application::cherche_itineraire(double x1,double y1,double x2,double y2) {
	Intersection *deb,*fin;
	reseau->projection(x1,y1,deb);
	reseau->projection(x2,y2,fin);
	if (!deb) affiche("Impossible de trouver le sommet de départ",ERREUR,"");
	else if (!fin) affiche("Impossible de trouver le sommet d'arrivée",ERREUR,"");
	else cherche_itineraire(deb,fin);
}

list<Arc*> Application::get_itineraire(double x1,double y1,double x2,double y2) {
	Intersection *deb,*fin;
	reseau->projection(x1,y1,deb);
	reseau->projection(x2,y2,fin);
	if (!deb) affiche("Impossible de trouver le sommet de départ",ERREUR,"");
	else if (!fin) affiche("Impossible de trouver le sommet d'arrivée",ERREUR,"");
	else return get_itineraire(deb,fin);
}

list<Arc*> Application::get_itineraire(Sommet* s1,Sommet* s2) {
	list<Arc*> chemin=reseau->itineraire(s1,s2,heuristique);
	return chemin;
}

void Application::compare_itineraire(double x1,double y1,double x2,double y2){
	double poids=0.0;
	ostringstream oss;
	ostringstream oss2;
	ofstream fichier;
	string tmp = "";

	if(config.fic_osiris_itin != ""){
		fichier.open(config.fic_osiris_itin.c_str(), ios::out | ios::trunc);

		if(!fichier.is_open()) return;
	}

	// calcule l'itinéraire avec l'eau sur les routes
	list<Arc*> chemin_avec_eau = get_itineraire(x1,y1,x2,y2);
	if (chemin_avec_eau.size()==0){
		affiche("Il n'existe pas d'itinéraire entre le point de départ et le point d'arrivée",INFORMATION,"Itinéraire introuvable");
		return;
	}
	reseau->init_couleurs();

	// sauvegarde les échelles
	list<Point> echelles_copy (echelles);
	list<double> niveaux_copy (niveaux);

	// enlève l'eau
	echelles.clear();
	niveaux.clear();
	calcule_poids();

	// calcule l'itinéraire par défaut, c'est à dire sans eau sur les routes
	list<Arc*> chemin_sans_eau = get_itineraire(x1,y1,x2,y2);

	// remet les échelles comme au départ pour la suite des commandes
	echelles.assign(echelles_copy.begin(),echelles_copy.end());
	niveaux.assign(niveaux_copy.begin(),niveaux_copy.end());
	calcule_poids();

	oss << "Itinéraire :" << endl;
	for (list<Arc*>::iterator it=chemin_avec_eau.begin();it!=chemin_avec_eau.end();it++) {
		Troncon *arc=(Troncon*)(*it);
		arc->tag=5; // vert => itinéraire à suivre

		if(config.fic_osiris_itin != ""){
			for (deque<Point*>::iterator itt = arc->begin(); itt!=arc->end();itt++) {
				Point *p=(Point*)(*itt);
				oss2 << std::fixed << p->x << "," << -(p->y);
				tmp += oss2.str() + " ";
				oss2.str("");
			}
			tmp += "^";
		}
		if(config.weight_unit == "h:m:s") oss << convert_time(arc->poids);
		else if(config.weight_unit != "") oss << arc->poids << config.weight_unit;
		else oss << arc->poids << "u";

		for (list<string>::iterator its=affichage_troncon.begin();its!=affichage_troncon.end();its++) {
			oss << ", ";
			arc->display(oss,(*its));
		}
		oss << "\n";
		poids+=(*it)->poids;
	}
	if(config.weight_unit == "h:m:s") oss << "TEMPS total : " << convert_time(poids) << endl;
	else if(config.weight_unit != "") oss << "Poids total : " << poids << config.weight_unit << endl;
	else oss << "Poids total : " << poids << "u" << endl;

	if(config.fic_osiris_itin != ""){
		fichier << tmp << endl;
	}
	affiche(oss.str(),TEXTE,"");

	tmp = "";
	oss2.str("");

	for (list<Arc*>::iterator it=chemin_sans_eau.begin();it!=chemin_sans_eau.end();it++) {
		Troncon *arc=(Troncon*)(*it);
		if(arc->tag != 5){ // si itinéraire différent que celui qu'il faut prendre avec l'eau
			arc->tag = 6; // rouge => modif de l'itinéraire par défaut

			if(config.fic_osiris_itin != ""){
				for (deque<Point*>::iterator itt = arc->begin(); itt!=arc->end();itt++) {
					Point *p=(Point*)(*itt);
					oss2 << std::fixed << p->x << "," << -(p->y);
					tmp += oss2.str() + " ";
					oss2.str("");
				}
				tmp += "^";
			}
		}
	}
	if(config.fic_osiris_itin != ""){
		fichier << tmp << endl;
		fichier << oss.str();
		fichier.close();
	}
}

void Application::calcule_tournee(int mode,int nb_iter,int nb_fourmis,double alpha,double beta,double rho) {
	const size_t MAX_NOEUDS=3;	//Nombre de noeuds au-delà duquel on applique l'algorithme de colonies de fourmis. S'il y a MAX_NOEUDS enjeux ou moins, l'algorithme de résolution est exact et parcourt l'ensemble des possibilités. S'il y a strictement plus de MAX_NOEUDS, on utilise la méthode d'intelligence artficielle des colonies de fourmis pour réduire le temps de calcul.
	if (enjeux.size()==0 || sources.size()==0) {
		affiche("Le calcul de tournée nécessite la définition d'au moins une source et d'un enjeu",ERREUR,"");
		return;
	}
	// Projection de tous les points sur le réseau
	vector<Intersection*> senjeux;	//Liste des sommets les plus proches des enjeux comprenant la source qui est traitée comme un enjeu à part entière
	vector<Ponctuel*> slinks;	//En correspondance avec senjeux, liens vers les enjeux
	Intersection *inter;
	if (reseau->projection(sources.front()->x,sources.front()->y,inter)>=0) {
		senjeux.push_back(inter);
		slinks.push_back(sources.front());
	} else {
		affiche_erreur("Impossible de trouver la source",ERREUR,"");
		return;
	}
	for (list<Ponctuel*>::iterator it=enjeux.begin();it!=enjeux.end();it++) {
		if (reseau->projection((*it)->x,(*it)->y,inter)>=0) {
			senjeux.push_back(inter);
			slinks.push_back(*it);
		} else {
			affiche_erreur("Impossible de trouver l'enjeu " + (*it)->get_string(0),ERREUR,"");
			return;
		}
	}

	ostringstream oss2;
	string tmp = "";
	ofstream fichier;

	if(config.fic_osiris_tourn != ""){
		fichier.open(config.fic_osiris_tourn.c_str(), ios::out | ios::trunc);

		if(!fichier.is_open()) return;
	}

	// Préparation du graphe qui doit être parcouru par les fourmis. Il s'agit d'un graphe complet reliant chaque enjeu à tous les autres et à la source.
	AntGraph ag;
	int num=1;
	list<Sommet*> origines;
	origines.push_back(senjeux.front());
	reseau->dijkstra_graphe(origines);
	vector<Ponctuel*>::iterator jtpp=slinks.begin();
	Sommet *an=new Sommet(*(senjeux.front()));	//Création du sommet correspondant à la source
	an->id=0;
	ag.sommets.push_back(an);
	vector<Intersection*>::iterator it=senjeux.begin();
	it++;
	while (it!=senjeux.end()) {	//Création des autres sommets avec les arcs issus de la source
		if ((*it)->dist>=0) {	//On crée les sommets en n'incluant que ceux qui sont accessibles depuis la source (sinon il ne sera jamais possible de les atteindre)
			an=new Sommet(**it);
			an->id=num;
			ag.sommets.push_back(an);
			AntEdge *ae=new AntEdge(0,ag.sommets.front(),an,(*it)->dist,"");
			ag.sommets.front()->arcs.push_back(ae);
			ag.arcs.push_back(ae);
			num++;
		} else {
			senjeux.erase(it);
			slinks.erase(jtpp);
			affiche("L'enjeu \""+ (*jtpp)->get_string(0) + "\" n'est pas accessible, il est retiré du parcours.",INFORMATION,"Calcul de tournée");
		}
		it++;jtpp++;
	}
	it=senjeux.begin();
	it++;
	vector<Sommet*>::iterator its1=ag.sommets.begin();
	its1++;
	while (its1!=ag.sommets.end()) {	//Création de tous les autres arcs
		origines.clear();
		origines.push_back(*it);
		reseau->dijkstra_graphe(origines);
		vector<Intersection*>::iterator it2=senjeux.begin();
		for (vector<Sommet*>::iterator its2=ag.sommets.begin();its2!=ag.sommets.end();its2++,it2++) if (its1!=its2) {
			if ((*it2)->dist>=0) {	//Si le sommet a bien été atteint par l'algorithme de Dijkstra
				AntEdge *ae=new AntEdge(0,*its1,*its2,(*it2)->dist,"");
				(*its1)->arcs.push_back(ae);
				ag.arcs.push_back(ae);
			}
		}
		its1++;it++;
	}
	// Recherche de la tournée optimale par l'algorithme le plus approprié
	list<Arc*> best;
	if (mode==1 || (mode==0 && enjeux.size()<=MAX_NOEUDS)) best=ag.tournee(ag.sommets.front());
	else best=ag.shortest_tour(nb_iter,nb_fourmis,ag.sommets.front(),alpha,beta,rho);
	// Affichage et représentation de la tournée optimale
	if (best.size()==0) {
		affiche("Aucune tournée trouvée",INFORMATION,"");
		return;
	}
	list<Arc*>::iterator itp=best.begin();
	Intersection *ideb=senjeux[best.front()->orig->id];
	num=1;
	double poids=0.0;
	ostringstream oss;
	oss << "Feuille de route de : " << sources.front()->get_string(0) << "\n";
	while (itp!=best.end()) {
		Intersection *ifin=senjeux[(*itp)->dest->id];
		list<Arc*> chemin=reseau->itineraire(ideb,ifin,heuristique);
		vector<Ponctuel*>::iterator jtp=slinks.begin();
		for (int k=0;k<(*itp)->dest->id;++k) jtp++;	//Recherche de l'enjeu à atteindre pendant l'étape en cours de traitement, à partir de l'identifiant de l'extrémité de l'arc
		oss << "- Etape " << num << " : " << (*jtp)->get_string(0) << endl;
		for (list<Arc*>::iterator it=chemin.begin();it!=chemin.end();it++) {
			((Troncon*)(*it))->tag=num;
			Troncon *arc=(Troncon*)(*it);

			if(config.fic_osiris_tourn != ""){
				for (deque<Point*>::iterator itt = arc->begin(); itt!=arc->end();itt++) {
					Point *p=(Point*)(*itt);
					oss2 << std::fixed << p->x << "," << -(p->y);
					tmp += oss2.str() + " ";
					oss2.str("");
				}
				tmp += "^";
			}

			if(config.weight_unit == "h:m:s") oss << convert_time(arc->poids);
			else if(config.weight_unit != "") oss << arc->poids << config.weight_unit;
			else oss << arc->poids << "u";

			for (list<string>::iterator its=affichage_troncon.begin();its!=affichage_troncon.end();its++) {
				oss << ", ";
				arc->display(oss,(*its));
			}
			oss << "\n";
			poids+=(*it)->poids;
		}
		ideb=ifin;
		itp++;
		num++;
		oss << "\n";
	}
	if(config.weight_unit == "h:m:s") oss << "===> TEMPS TOTAL : " << convert_time(poids);
	else if(config.weight_unit != "")  oss << "POIDS TOTAL : " << poids << config.weight_unit;
	else oss << "POIDS TOTAL : " << poids << "u";

	if(config.fic_osiris_tourn != ""){
		fichier << tmp << endl;
		fichier << oss.str();
		fichier.close();
	}
	affiche(oss.str(),TEXTE,"");
}


string Application::convert_time(double temps_minutes){
	int sec,secondes,minutes,heures,temp;
	ostringstream oss;

	sec = round(temps_minutes*60);
	temp = sec % 3600;
	heures = ( sec - temp ) / 3600 ;
	secondes = temp % 60 ;
	minutes = ( temp - secondes ) / 60;

	if(heures > 0){
		oss << heures << "h ";
	}
	if(minutes > 0 || (minutes == 0 && heures > 0)){
		oss << minutes << "min ";
	}
	if(heures == 0){
		oss << secondes << "s ";
	}

	return oss.str();
}


void Application::calcule_desserte() {
	// Constitution de la liste des sommets sources en cherchant les sommets les plus proches des coordonnées données par l'utilisateur
	list<Sommet*> s;
	Intersection *inter;
	for (list<Ponctuel*>::iterator it=sources.begin();it!=sources.end();it++) {
		if (reseau->projection((*it)->x,(*it)->y,inter)>=0) {
			s.push_back(inter);
		}
	}
	reseau->desserte(s);
	// Mise à jour du membre Intersection::tag avec le numéro de l'origine correspondant au sommet
	int num=0;
	for (list<Sommet*>::iterator it=s.begin();it!=s.end();it++) {
		((Intersection*)(*it))->tag=num++;
	}
	for (vector<Sommet*>::iterator it=reseau->sommets.begin();it!=reseau->sommets.end();it++) {
		Intersection *inte=(Intersection*)(*it);
		if (inte->source()) inte->tag=((Intersection*)(inte->source()))->tag; else inte->tag=-1;
	}
}

void Application::dessine_accessibilite(double resolution,double vmax) {
	if (sources.size()==0) return;
	if (calcule_raster) {
		calcule_desserte();
		calcule_raster=false;
	}
	// Création de la carte raster de desserte après avoir éventuellement supprimé la carte précédente
	delete raster;
	if (resolution<(w_xmax-w_xmin)/MAX_TAILLE) resolution=(w_xmax-w_xmin)/MAX_TAILLE;
	RasterContinu *rasterc=new RasterContinu((int)((w_xmax-w_xmin)/resolution),(int)((w_ymax-w_ymin)/resolution),w_xmin,w_ymin,resolution,0,vmax);
	double x=w_xmin;
	double y;
	for (int i=0;i<rasterc->taillex();++i) {
		y=w_ymin;
		for (int j=0;j<rasterc->tailley();++j) {
			/*Intersection *inter;
			double dist=reseau->projection(x,y,inter,accessible);
			if (inter==0) (*rasterc)(i,j)=-1.0;	// Valeur spéciale pour indiquer que le sommet voisin n'est pas accessible
			else (*rasterc)(i,j)=inter->dist+penalite*dist;	// La distance totale vaut la distance du plus proche sommet à une source plus la distance euclidienne du point à ce plus proche sommet*/
			Intersection *inter[3];
			double dist[3];
			double ppoint[2]={x,y};
			reseau->arbre->neighbours(3,ppoint,inter,dist,accessible);
			int k=0;
			while (k<3 && inter[k]!=0) {
				dist[k]=sqrt(dist[k]);	// On en profite pour extraire les distances car la fonction de calcul des voisins renvoie les carrés
				++k;	// k contient le nombre de voisins trouvés
			}
			if (k==0) (*rasterc)(i,j)=-1.0;
			else if (k==1) (*rasterc)(i,j)=inter[0]->dist+penalite*dist[0];	// S'il n'y a qu'un point, la distance correspond à la distance du sommet trouvé à la source ajoutée de la distance à ce sommet affectée d'un coefficient de pénalité
			else if (k==2) {	// S'il n'y a que deux points A et B, la distance correspond à une interpolation de la distance à ces deux points, ajoutée de la distance à la droite AB affectée d'un coefficient de pénalité
				double beta=(x-inter[0]->x)*(inter[1]->x-inter[0]->x)+(y-inter[0]->y)*(inter[1]->y-inter[0]->y);
				double ab=(inter[1]->x-inter[0]->x)*(inter[1]->x-inter[0]->x)+(inter[1]->y-inter[0]->y)*(inter[1]->y-inter[0]->y);
				beta=fabs(beta)/ab;
				double dd=Point(x,y).distance_segment(*(inter[0]),*(inter[1]));	//dd contient la distance au segment AB
				(*rasterc)(i,j)=inter[0]->dist+beta*(inter[1]->dist-inter[0]->dist);
				for (int l=0;l<2;++l) if ((*rasterc)(i,j)>inter[l]->dist+penalite*dist[l]) (*rasterc)(i,j)=inter[l]->dist+penalite*dist[l];
				double mm=DBL_MAX;
				for (int l=0;l<2;++l) {if (mm>inter[l]->dist) mm=inter[l]->dist;}
				if ((*rasterc)(i,j)<mm) (*rasterc)(i,j)=mm;
				(*rasterc)(i,j)+=penalite*dd;
			}
			else if (k==3) {	// S'il y a trois points A, B et C, la distance correspond à une interpolation linéaire de la distance à ces trois points, à laquelle on ajoute la distance à la droite AB affectée d'un coefficient de pénalité
				double det=(inter[1]->x-inter[0]->x)*(inter[2]->y-inter[0]->y)-(inter[1]->y-inter[0]->y)*(inter[2]->x-inter[0]->x);
				double beta=((x-inter[0]->x)*(inter[2]->y-inter[0]->y)-(y-inter[0]->y)*(inter[2]->x-inter[0]->x))/det;
				double gamma=((inter[1]->x-inter[0]->x)*(y-inter[0]->y)-(inter[1]->y-inter[0]->y)*(x-inter[0]->x))/det;
				double dd=Point(x,y).distance_segment(*(inter[0]),*(inter[1]));	//dd contient la distance au segment AB
				(*rasterc)(i,j)=inter[0]->dist+beta*(inter[1]->dist-inter[0]->dist)+gamma*(inter[2]->dist-inter[0]->dist);
				for (int l=0;l<3;++l) if ((*rasterc)(i,j)>inter[l]->dist+penalite*dist[l]) (*rasterc)(i,j)=inter[l]->dist+penalite*dist[l];
				double mm=DBL_MAX;
				for (int l=0;l<3;++l) {if (mm>inter[l]->dist) mm=inter[l]->dist;}
				if ((*rasterc)(i,j)<mm) (*rasterc)(i,j)=mm;
				(*rasterc)(i,j)+=penalite*dd;
			}
			y+=resolution;
		}
		x+=resolution;
	}
	raster=rasterc;
}

void Application::dessine_desserte(double resolution) {
	if (sources.size()==0) return;
	if (calcule_raster) {
		calcule_desserte();
		calcule_raster=false;
	}
	// Création de la carte raster de desserte après avoir éventuellement supprimé la carte précédente
	delete raster;
	if (resolution<(w_xmax-w_xmin)/MAX_TAILLE) resolution=(w_xmax-w_xmin)/MAX_TAILLE;
	RasterDiscret *rasterd=new RasterDiscret(sources.size(),(int)((w_xmax-w_xmin)/resolution),(int)((w_ymax-w_ymin)/resolution),w_xmin,w_ymin,resolution);
	double x=w_xmin;
	double y;
	for (int i=0;i<rasterd->taillex();++i) {
		y=w_ymin;
		for (int j=0;j<rasterd->tailley();++j) {
			Intersection *inter;
			reseau->projection(x,y,inter,accessible);
			if (inter==0) (*rasterd)(i,j)=-1;	// Valeur spéciale pour indiquer que le sommet voisin n'est pas accessible
			else (*rasterd)(i,j)=inter->tag;	// La distance totale vaut la distance du plus proche sommet à une source plus la distance euclidienne du point à ce plus proche sommet
			y+=resolution;
		}
		x+=resolution;
	}
	raster=rasterd;
}

void Application::dessine_cote(double resolution,double vmax) {
	// Création de la carte raster des cotes d'eau après avoir éventuellement supprimé la carte précédente
	delete raster;
	if (resolution<(w_xmax-w_xmin)/MAX_TAILLE) resolution=(w_xmax-w_xmin)/MAX_TAILLE;
	RasterContinu *rasterc=new RasterContinu((int)((w_xmax-w_xmin)/resolution),(int)((w_ymax-w_ymin)/resolution),w_xmin,w_ymin,resolution,0,vmax);
	double x=w_xmin;
	double y;
	for (int i=0;i<rasterc->taillex();++i) {
		y=w_ymin;
		for (int j=0;j<rasterc->tailley();++j) {
			y+=resolution;
			(*rasterc)(i,j)=evalue_cote(x,y);
		}
		x+=resolution;
	}
	raster=rasterc;
}

void Application::importe_raster(string fichier) {
	delete raster;
	try {
		RasterContinu *rasterc=new RasterContinu(fichier,w_xmin,w_ymin,w_xmax,w_ymax);
		raster=rasterc;
		affiche("Carte raster importée depuis "+fichier,INFORMATION,"Import réussi");
	} catch (exception e) {
		affiche("Erreur lors de l'importation. "+string(e.what()),ERREUR,"Echec de l'import");
	}
}

void Application::exporte_raster(string fichier) const {
	if (raster) {
		raster->exporte(fichier);
		affiche("Carte raster exportée sur "+fichier,INFORMATION,"Export réussi");
	}
}

void Application::exporte_svg(string fichier){
	ofstream svg;
	ostringstream points;

	if(fichier != ""){
		svg.open(fichier.c_str(), ios::out | ios::trunc);

		if(!svg.is_open()) return;
	}

	for (vector<Arc*>::iterator it=reseau->arcs.begin();it!=reseau->arcs.end();it++) {
		points.str("");
		Troncon *arc=(Troncon*)(*it);

		string sens_val=arc->get_string(config.champ_sens); // sens de circulation sur le troncon courant
		int nb_pts = arc->num_points();

		if(nb_pts > 2){ // polyligne
			for (deque<Point*>::iterator itt = arc->begin(); itt!=arc->end();itt++) {
				Point *p=(Point*)(*itt);
				points << std::fixed << p->x << "," << -(p->y) << " ";
			}
			svg << "polyline ^" << points.str();
			if(arc->coupe) svg << "^fill:none;stroke:#" << config.couleur_coupe << ";stroke-width:6" << endl;
			else svg << "^fill:none;stroke:black;stroke-width:4" << endl;

		}
		else if(nb_pts == 2){ // ligne
			deque<Point*>::iterator itt = arc->begin();
			deque<Point*>::iterator itt2 = itt+1;
			Point *a=(Point*)(*itt);
			Point *b=(Point*)(*itt2);
			svg << std::fixed << "line " << a->x << " " << -(a->y) << " " << b->x << " " << -(b->y);
			if(arc->coupe) svg << " fill:none;stroke:#" << config.couleur_coupe << ";stroke-width:6" << endl;
			else svg << " fill:none;stroke:black;stroke-width:4" << endl;
		}

		// double sens -> on saute le troncon suivant qui est le meme dans l'autre sens
		if (sens_val.compare(config.sens_unique)!=0){
			it++;
		}
	}

	if(fichier != ""){
		svg.close();
	}
}
